#include "app_device.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "app_common.h"
#include "app_buffer.h"
#include "app_mqtt.h"
#include "app_pool.h"
#include "app_message.h"
#include "app_bt.h"
#include "log/log.h"

#define BUFFER_SIZE 1024
#define POOL_SIZE 5
#define WRITE_INTERVAL_MS 200

static Device_t *device = NULL;

/**
 * @brief 初始化设备（只需要一个网关设备）
 *
 * @param filename 蓝牙文件指针
 * @return int 成功返回0，失败返回-1
 */
int app_device_init(char *filename)
{
    // 检查设备是否已经初始化
    if (device != NULL)
    {
        return 0;
    }

    // 申请设备内存
    device = (Device_t *)malloc(sizeof(Device_t));

    device->filename = filename;
    device->fd = open(device->filename, O_RDWR);

    device->up_buffer = app_buffer_init(BUFFER_SIZE);
    device->down_buffer = app_buffer_init(BUFFER_SIZE);

    device->is_runninng = false;

    // device->last_write_time = 0;

    device->post_read = NULL;
    device->pre_write = NULL;

    // 初始化其他模块
    app_bt_init(device);
    app_pool_init(POOL_SIZE);
    app_mqtt_init();
    
    return 0;
}

// 发送消息到远程的任务函数==》由线程池中某个线程执行
static int send_task_func(void *arg)
{
    // 从上行缓冲区读取数据
    char data_buf[128];
    int len = app_buffer_read(device->up_buffer, data_buf, 128);
    if (len > 0)
    {
        // 将数据转换成json格式
        char *json = app_message_charsToJson(data_buf, len);
        // 发送json格式数据到远程
        int ret = app_mqtt_sendMes(json);
        if (ret != 0)
        {
            log_error("发送数据到远程失败...");
            return -1;
        }
        return 0;
    }
    return 0;
}

// 读线程函数
static void *read_thread_func(void *arg)
{
    while (device->is_runninng)
    {
        // 读文件数据
        char data_buf[128];
        int len = read(device->fd, data_buf, 128);
        if (len > 0)
        {
            // 回调函数处理数据
            if (device->post_read)
            {
                // 将数据处理成字符数组
                len = device->post_read(data_buf, len);
                // 代表数据处理完毕，可以发送到远程
                if (len > 0)
                {
                    // 写数据到缓冲区
                    app_buffer_write(device->up_buffer, data_buf, len);
                    // 注册发送消息到远程的任务函数
                    app_pool_registerCallback(send_task_func, NULL);
                }
            }

            // // 写数据到上行缓冲区
            // app_buffer_write(device->up_buffer, data_buf, len);
            // // 注册发送消息到远程的任务函数
            // app_pool_registerCallback(send_task_func, NULL);
        }
    }
}

static long temp_time = 0;
// 写数据到设备的任务函数==》由线程池中某个线程执行
static int write_task_func(void *arg)
{
    // 从下行缓冲区读取数据
    char data_buf[128];
    int len = app_buffer_read(device->down_buffer, data_buf, 128);
    if (len > 0)
    {
        // 回调函数处理数据
        if (device->pre_write)
        {
            // 将数据处理成设备需要的数据格式
            len = device->pre_write(data_buf, len,128);
            if (len > 0)
            {
                // 两次写入间隔不能太短
                long current_time = app_common_getCurrentTime();
                long interval = current_time - temp_time;
                // 提前更新temp_time，防止两次写入间隔太短导致设备数据丢失覆盖
                if (interval < WRITE_INTERVAL_MS)
                {
                    temp_time = current_time + (WRITE_INTERVAL_MS - interval);
                    usleep((WRITE_INTERVAL_MS - interval) * 1000);
                }
                else
                {
                    // 解决第一次temp_time没有赋值问题
                    temp_time = current_time;
                }

                // 更新最后写入时间
                // device->last_write_time = app_common_getCurrentTime();

                // 写入设备文件
                int write_len = write(device->fd, data_buf, len);
                if (write_len != len)
                {
                    log_error("写入设备文件失败");
                    return -1;
                }

                log_debug("向下游设备发送数据成功：%s", app_common_charsToHexstr(data_buf, len));
                return 0;
            }
        }
        log_error("写前的回调函数不存在...");
        return -1;

        // // 两次写入间隔不能太短
        // long current_time = app_common_getCurrentTime();
        // long interval = current_time - temp_time;
        // // 提前更新temp_time，防止两次写入间隔太短导致设备数据丢失覆盖
        // if (interval < WRITE_INTERVAL_MS)
        // {
        //     temp_time = current_time + WRITE_INTERVAL_MS - interval;
        //     usleep((WRITE_INTERVAL_MS - interval) * 1000);
        // }
        // else
        // {
        //     // 解决第一次temp_time没有赋值问题
        //     temp_time = current_time;
        // }

        // // 更新最后写入时间
        // device->last_write_time = app_common_getCurrentTime();

        // // 写入设备文件
        // int write_len = write(device->fd, data_buf, len);
        // if (write_len != len)
        // {
        //     log_error("写入设备文件失败");
        //     return -1;
        // }

        // log_debug("向下游设备发送数据成功：%s", app_common_charsToHexstr(data_buf, len));
        // return 0;
    }
    return 0;
}

// 接收到mqtt消息的回调函数
static int receive_mes_callback(char *json)
{
    // 将json格式数据转换成字符数组
    char data_buf[128];
    int len = app_message_jsonToChars(json, data_buf, 128);
    if (len > 0)
    {
        // 将字符数组写入下行缓冲区
        app_buffer_write(device->down_buffer, data_buf, len);
        // 注册发送数据到设备的任务函数
        app_pool_registerCallback(write_task_func, NULL);
    }
    return 0;
}

/**
 * @brief 启动设备
 *
 * @return int 成功返回0，失败返回-1
 */
int app_device_start(void)
{
    // 检查设备是否已经启动
    if (device->is_runninng)
    {
        log_debug("设备已经启动...");
        return 0;
    }

    device->is_runninng = true;

    // 创建读线程
    pthread_create(&device->read_thread, NULL, read_thread_func, NULL);

    // 注册回调函数读取mqtt数据
    app_mqtt_registerRecvCallback(receive_mes_callback);

    return 0;
}

/**
 * @brief 停止设备
 *
 * @return int 成功返回0，失败返回-1
 */
int app_device_stop(void)
{
    device->is_runninng = false;
    pthread_cancel(device->read_thread);

    close(device->fd);

    app_buffer_free(device->down_buffer);
    app_buffer_free(device->up_buffer);

    device->post_read = NULL;
    device->pre_write = NULL;
    // device->last_write_time = 0;

    app_mqtt_deinit();
    app_pool_close();

    return 0;
}